using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;

namespace AlgorithmsVisualized.MathHelp
{
    public class Matrix
    {
        double[,] m_values;
        int width, height;
        public Matrix(int width, int height)
        {
            m_values = new double[height, width];
            this.width = width;
            this.height = height;
        }
        public double[,] Data
        {
            get
            {
                return m_values;
            }
            set
            {
                m_values = value;
            }
        }
        public double this[int row, int col]
        {
            get
            {
                return Data[row, col];
            }
            set
            {
                Data[row, col] = value;
            }
        }
        public static Matrix operator*(Matrix m1, Matrix m2)
        {
            if (m1.width != m2.height)
                throw new Exception("Unexpectable operation");
            Matrix m = new Matrix(m2.width, m1.height);
            for (int i = 0; i < m1.height; i++)
                for (int j = 0; j < m2.width; j++)
                    for (int k = 0; k < m1.width; k++)
                        m.Data[i,j] += m1.Data[i,k] * m2.Data[k,j];
            return m;
        }
        public static Matrix Identity(int diminsion)
        {
            Matrix m = new Matrix(diminsion, diminsion);
            for (int i = 0; i < diminsion; i++)
                for (int j = 0; j < diminsion; j++)
                    if (i == j)
                        m.Data[i, j] = 1;
                    else
                        m.Data[i, j] = 0;
            return m;
        }
        public static Matrix Translation(double tx, double ty)
        {
            Matrix m = Matrix.Identity(3);
            m.Data[0, 2] = tx;
            m.Data[1, 2] = ty;
            return m;
        }
        public static Matrix Scalling(double sx, double sy)
        {
            Matrix m = Matrix.Identity(3);
            m.Data[0, 0] = sx;
            m.Data[1, 1] = sy;
            return m;
        }
        public static Matrix Rotation(double angle)
        {
            Matrix m = Matrix.Identity(3);
            double ang = angle * Math.PI/180.0f;
            double Cos = Math.Cos(ang);
            double Sin = Math.Sin(ang);

            m.Data[0, 0] = Cos;
            m.Data[0, 1] = -Sin;
            m.Data[1, 0] = Sin;
            m.Data[1, 1] = Cos;
            return m;
        }
        public static Matrix Shearing(double shearY, double shearX)
        {
            Matrix m = Matrix.Identity(3);
            m.Data[0, 1] = shearY;
            m.Data[1, 0] = shearX;
            return m;
        }
        public static Matrix ReflectionAroundX()
        {
            return Reflection(1, -1);
        }
        public static Matrix ReflectionAroundY()
        {
            return Reflection(-1, 1);
        }
        public static Matrix ReflectionAboutOrigin()
        {
            return Reflection(-1, -1);
        }
        public static Matrix Reflection(double x, double y)
        {
            Matrix m = Matrix.Identity(3);
            m.Data[0, 0] = x;
            m.Data[1, 1] = y;
            return m;
        }
        public static Matrix FromPoint(Point p)
        {
            Matrix m = new Matrix(1, 3);
            m.Data[0, 0] = p.X;
            m.Data[1, 0] = p.Y;
            m.Data[2, 0] = 1;
            return m;
        }
        public static Point FromMatrix(Matrix m)
        {
            Point p = new Point((int)m.Data[0, 0], (int)m.Data[1, 0]);
            return p;
        }
/*
m11*m22 - m12*m21 m02*m21 - m01*m22 m01*m12 - m02*m11 
m12*m20 - m10*m22 m00*m22 - m02*m20 m02*m10 - m00*m12 
m10*m21 - m11*m20 m01*m20 - m00*m21 m00*m11 - m01*m10 
*/
        /// <summary>
        /// Inverse for 3 * 3 Matrics only
        /// </summary>
        /// <param name="m">The 3 * 3 Matrix to get its inverse</param>
        /// <returns>A new matrix equivilant to the inverse of m</returns>
        public static Matrix MatrixInverse(Matrix m)
        {
            Matrix mInv = Matrix.Identity(3);
            mInv[0, 0] = m[1, 1] * m[2, 2] - m[1, 2] * m[2, 1];
            mInv[0, 1] = m[0, 2] * m[2, 1] - m[0, 1] * m[2, 2];
            mInv[0, 2] = m[0, 1] * m[1, 2] - m[0, 2] * m[1, 1];
            mInv[1, 0] = m[1, 2] * m[2, 0] - m[1, 0] * m[2, 2];
            mInv[1, 1] = m[0, 0] * m[2, 2] - m[0, 2] * m[2, 0];
            mInv[1, 2] = m[0, 2] * m[1, 0] - m[0, 0] * m[1, 2];
            mInv[2, 0] = m[1, 0] * m[2, 1] - m[1, 1] * m[2, 0];
            mInv[2, 1] = m[0, 1] * m[2, 0] - m[0, 0] * m[2, 1];
            mInv[2, 2] = m[0, 0] * m[1, 1] - m[0, 1] * m[1, 0];
            return mInv;
        }
        public Matrix Inverse
        {
            get
            {
                return Matrix.MatrixInverse(this);
            }
        }
        public static System.Drawing.Drawing2D.Matrix GetDrawingMatrix(Matrix m)
        {
            return new System.Drawing.Drawing2D.Matrix((float)m[0, 0], (float)m[0, 1], (float)m[1, 0], (float)m[1, 1], 1, 1);
        }
    }
}
